// 待定：初始状态
const PENDING = 'PENDING'

// 已兑现：意味着操作成功
const FULFILLED = 'FULFILLED'

// 已拒绝：意味着操作失败
const REJECTED = 'REJECTED'

// 以构造函数的形式实现
class MyPromise {
  // 构造器，其中 executor 是一个函数，传入就会被执行
  constructor(executor) {
    // 立即执行 executor，并利用 try/catch 捕获错误
    try {
      // 根据 Promise 的用法：new Promise((resolve, reject) => {...}) ，executor 接收 resolve 和 reject 两个参数
      executor(this.resolve, this.reject)
    } catch (error) {
      // 如果捕获到 executor 执行错误，直接 reject()
      this.reject(error)
    }
  }
  // 定义 Promise 初始状态为 PENDING
  status = PENDING
  // 定义 resolve 后返回的数据
  data = undefined
  // 定义 resolve 的回调函数列表
  successCallback = []
  // 定义 reject 后返回的原因
  reason = undefined
  // 定义 reject 的回调函数列表
  failureCallback = []
  // 定义 resolve 函数。调用 resolve() 需要将状态改为 FULFILLED，并调用所有的 successCallback
  resolve = data => {
    // 只有当状态为 PEDING 时，才能改变状态（一旦状态改变，就不能再变）
    if (this.status === PENDING) {
      // 改为成功状态
      this.status = FULFILLED
      // 保存数据
      this.data = data
      // 依次调用成功回调
      while (this.successCallback.length) {
        this.successCallback.shift()(this.data)
      }
    }
  }
  // 失败，调用 reject() 需要将状态改为 REJECTED，并调用所有的 failureCallback
  reject = reason => {
    // 只有当状态为 PEDING 时，才能改变状态（一旦状态改变，就不能再变）
    if (this.status === PENDING) {
      // 改为失败状态
      this.status = REJECTED
      // 保存原因
      this.reason = reason
      // 依次调用失败回调
      while (this.failureCallback.length) {
        this.failureCallback.shift()(this.reason)
      }
    }
  }
  /**
   * @name then
   * @description then 可以同时处理成功（第一个参数）和失败（第二个参数）的情况
   * @param {Function} onFulfilled 成功回调
   * @param {Function} onRejected 失败回调
   * @return {MyPromise} 返回一个新的 Promise 对象，用于链式调用
   * @example
   * asyncFunction().then(
   *  data => {
   *   console.log(data)
   *  },
   *  error => {
   *   console.log(error)
   *  }
   * )
   */
  then(onResolved = data => data /*设置默认的成功回调 */, onRejected) {
    // 返回一个新的 Promise 对象
    return new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // 转换为 异步执行，用来获取 新的 promise
        setTimeout(() => {
          try {
            let value = onResolved(this.data)
            // 判断返回值是普通值还是 Promise
            resolvePromise(promise, value, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let value = onRejected(this.reason)
            resolvePromise(promise, value, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
      } else {
        // 将回调函数存入数组
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let value = onResolved(this.data)
              resolvePromise(promise, value, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0)
        })
        // 将回调函数存入数组
        this.failureCallback.push(() => {
          setTimeout(() => {
            try {
              let value = onRejected(this.reason)
              resolvePromise(promise, value, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0)
        })
      }
    })
  }
  // .catch()
  catch(onRejected) {
    // 事实上 .catch() 只是没有给 fulfilled 状态预留参数位置的 .then()
    return this.then(undefined, onRejected)
  }
  // .finally()
  finally(callback) {
    return this.then(
      data => {
        return MyPromise.resolve(callback().then(() => data))
      },
      err => {
        return MyPromise.resolve(callback()).then(() => {
          throw err
        })
      }
    )
  }
  // Promise.all()
  static all(iterable) {
    // 记录执行次数
    let times = 0
    // 保存执行结果
    let result = []
    // Promise.all() 会返回一个 Promise
    return new MyPromise((resolve, reject) => {
      // 记录结果
      function addData(key, value) {
        times++
        result[key] = value
        times === iterable.length && resolve(result)
      }
      // 依次执行，然后将结果保存到数组中
      iterable.forEach((element, index) => {
        // 判断元素是否为 Promise 对象
        element instanceof MyPromise
          ? element.then(
              data => addData(index, data),
              err => reject(err) // 任何一个 Promise 对象的 reject 被执行都会立即 reject()
            )
          : addData(index, element) // 非 promise 的元素将被直接放在返回数组中
      })
    })
  }
  // Promise.resolve()
  static resolve(value) {
    // 返回一个以给定值解析后的 Promise 对象
    return value instanceof MyPromise
      ? value
      : new MyPromise(resolve => resolve(value))
  }
  // Promise.reject()
  static reject(error) {
    return new MyPromise((resolve, reject) => {
      reject(error)
    })
  }
}

// 判断 Promise 的返回值类型
function resolvePromise(promise, value, resolve, reject) {
  // 自己等待自己完成，则报“循环引用”错误
  if (promise === value) {
    return reject(
      new TypeError('Chaining cycle detected for promise #<Promise>')
    )
  }
  // 如果是 Promise 对象
  if (value instanceof MyPromise) {
    value.then(resolve, reject)
  } else {
    resolve(value)
  }
}

module.exports = MyPromise
